package cn.jbt.api.admin.service.impl;

import cn.jbt.api.admin.entity.*;
import cn.jbt.api.admin.filter.LoginInfoAccessContext;
import cn.jbt.api.admin.repository.SysMenuRepository;
import cn.jbt.api.admin.repository.SysRoleMenuRepository;
import cn.jbt.api.admin.service.ISysMenuService;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

@Service
@Slf4j
@AllArgsConstructor
public class SysMenuServiceImpl implements ISysMenuService {

    private final JPAQueryFactory jpaQueryFactory;
    private final SysMenuRepository sysMenuRepository;
    private final SysRoleMenuRepository sysRoleMenuRepository;

    @Override
    public List<String> selectAllPermission() {

        QSysMenu qSysMenu = QSysMenu.sysMenu;

        return jpaQueryFactory.selectDistinct(qSysMenu.permission)
                .from(qSysMenu)
                .where(qSysMenu.status.eq(1).and(qSysMenu.permission.ne("")))
                .fetch();
    }

    @Override
    public List<String> selectAllPermissionByUserId(long userId) {
        QSysMenu qSysMenu = QSysMenu.sysMenu;
        QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
        QSysRole qSysRole = QSysRole.sysRole;
        QSysRoleMenu qSysRoleMenu = QSysRoleMenu.sysRoleMenu;

        return jpaQueryFactory.selectDistinct(qSysMenu.permission)
                .from(qSysMenu)
                .leftJoin(qSysRoleMenu).on(qSysMenu.id.eq(qSysRoleMenu.menuId))
                .leftJoin(qSysUserRole).on(qSysRoleMenu.roleId.eq(qSysUserRole.roleId))
                .leftJoin(qSysRole).on(qSysUserRole.roleId.eq(qSysRole.id))
                .where(qSysMenu.status.eq(1).and(qSysMenu.permission.ne(""))
                        .and(qSysUserRole.userId.eq(userId))
                        .and(qSysRole.status.eq(1)))
                .fetch();
    }

    @Override
    public List<SysMenu> getAllByUserId(long userId) {
        QSysMenu qSysMenu = QSysMenu.sysMenu;
        QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
        QSysRole qSysRole = QSysRole.sysRole;
        QSysRoleMenu qSysRoleMenu = QSysRoleMenu.sysRoleMenu;

        return jpaQueryFactory.selectDistinct(qSysMenu)
                .from(qSysMenu)
                .leftJoin(qSysRoleMenu).on(qSysMenu.id.eq(qSysRoleMenu.menuId))
                .leftJoin(qSysUserRole).on(qSysRoleMenu.roleId.eq(qSysUserRole.roleId))
                .leftJoin(qSysRole).on(qSysUserRole.roleId.eq(qSysRole.id))
                .where(qSysMenu.status.eq(1).and(qSysUserRole.userId.eq(userId))
                        .and(qSysRole.status.eq(1)))
                .orderBy(qSysMenu.floor.asc(), qSysMenu.orderby.asc(), qSysMenu.id.asc())
                .fetch();
    }

    @Override
    public List<SysMenu> getAll(SysMenu sysMenu) {
        QSysMenu qSysMenu = QSysMenu.sysMenu;

        Predicate predicate = qSysMenu.isNotNull().or(qSysMenu.isNull());
        if (null != sysMenu) {
            if (!StringUtils.isEmpty(sysMenu.getMenuName())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysMenu.menuName.contains(sysMenu.getMenuName()));
            }
            if (null != sysMenu.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysMenu.status.eq(sysMenu.getStatus()));
            }
        }

        return jpaQueryFactory.selectFrom(qSysMenu)
                .where(predicate)
                .orderBy(qSysMenu.floor.asc(), qSysMenu.orderby.asc(), qSysMenu.id.asc())
                .fetch();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysMenu add(SysMenu sysMenu) {
        sysMenu.setCreateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysMenu.setUpdateBy(sysMenu.getCreateBy());
        sysMenu = sysMenuRepository.save(sysMenu);

        // update floor、node
        if (sysMenu.getPid() > 0) {
            SysMenu parent = sysMenuRepository.findFirstById(sysMenu.getPid());
            if (null == parent) {
                throw new RuntimeException("父级未找到");
            }
            sysMenu.setFloor(parent.getFloor() + 1);
            sysMenu.setNode(parent.getNode() + sysMenu.getId() + "/");
        } else {
            sysMenu.setFloor(1);
            sysMenu.setNode(sysMenu.getId() + "/");
        }

        return sysMenuRepository.save(sysMenu);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysMenu edit(SysMenu sysMenu) {
        SysMenu newSysMenu = sysMenuRepository.findFirstById(sysMenu.getId());
        if (null == newSysMenu) {
            throw new RuntimeException("404 NotFound");
        }

        newSysMenu.edit(sysMenu);
        newSysMenu.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        return sysMenuRepository.save(newSysMenu);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(List<Long> ids) {
        for (Long id : ids) {
            if (null != sysMenuRepository.findFirstByPid(id)) {
                throw new RuntimeException("请先删除下级");
            }
        }

        sysMenuRepository.deleteAllByIdInBatch(ids);
        sysRoleMenuRepository.deleteByMenuIdIn(ids);
    }

    @Override
    public String getCascadeNamesById(long id, String delimiter) {
        SysMenu sysMenu = sysMenuRepository.findFirstById(id);
        if (null == sysMenu) {
            return "";
        }

        List<Long> ids = Arrays.stream(sysMenu.getNode().split("/"))
                .filter(StringUtils::isNotEmpty)
                .map(Long::parseLong)
                .toList();

        QSysMenu qSysMenu = QSysMenu.sysMenu;
        List<String> names = jpaQueryFactory.select(qSysMenu.menuName)
                .from(qSysMenu)
                .where(qSysMenu.id.in(ids))
                .orderBy(qSysMenu.floor.asc())
                .fetch();

        return String.join(delimiter, names);
    }
}
